﻿/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Dou.Utils;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;

namespace DouEditor.Utils
{
    /// <summary>
    /// 编辑器工具类
    /// </summary>
    public static class EditorUtil
    {
        private static readonly string ProjectPath;
        private static readonly Dictionary<Type, string> _uxmlDic = new Dictionary<Type, string>();
        
        static EditorUtil()
        {
            ProjectPath = FormatPath(Path.GetDirectoryName(Application.dataPath)) + "/";
        }
        
        public static string[] GetAllSelectedFile(bool absolutePath = false)
        {
            string[] guids = Selection.assetGUIDs;
            string[] result = new string[guids.Length];
            for (int i = 0; i < guids.Length; i++)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
                if (absolutePath)
                {
                    assetPath = assetPath.Substring(7);
                    result[i] = Application.dataPath + "/" + assetPath;
                }
                else
                {
                    result[i] = assetPath;
                }
            }
            return result;
        }

        public static bool ProcessCommand(string command, string arguments)
        {
            ProcessStartInfo start = new ProcessStartInfo(command)
            {
                Arguments = arguments,
                CreateNoWindow = true,
                ErrorDialog = true,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                StandardOutputEncoding = Encoding.UTF8,
                StandardErrorEncoding = Encoding.UTF8
            };
            Process process = Process.Start(start);
            if (process == null)
            {
                Debug.LogError("命令执行失败：" + command + " " + arguments);
                return false;
            }
            string info = process.StandardOutput.ReadToEnd();
            if (!string.IsNullOrEmpty(info))
            {
                Debug.Log(info);
            }
            string err = process.StandardError.ReadToEnd();
            if (!string.IsNullOrEmpty(err))
            {
                Debug.LogError(err);
            }
            process.WaitForExit();
            process.Close();
            return true;
        }

        public static T GetOrCreateSettings<T>(string filePath) where T : ScriptableObject
        {
            if (!File.Exists(filePath))
            {
                string directory = Path.GetDirectoryName(filePath);
                FilesUtil.CreateDirectoty(directory);
                AssetDatabase.CreateAsset(ScriptableObject.CreateInstance<T>(), filePath);
            }
            return AssetDatabase.LoadAssetAtPath<T>(filePath);
        }

        public static void SaveSettings(ScriptableObject settings)
        {
            EditorUtility.SetDirty(settings);
            AssetDatabase.SaveAssets();
        }
        
        public static void CreateDir(string path) {
            if (!Directory.Exists(path)) {
                Directory.CreateDirectory(path);
            }
        }
        
        public static void CreateDirByFilePath(string filePath) {
            var dir = Path.GetDirectoryName(filePath);
            CreateDir(dir);
        }

        /// <summary>
        /// 文件夹拷贝
        /// </summary>
        public static void CopyDir(string originPath, string copyPath, bool force = true)
        {
            var dirs = Directory.GetDirectories(originPath, "*", SearchOption.AllDirectories);
            foreach (var dir in dirs)
            {
                var relativePath = ConvertToRelativePath(originPath, dir);
                if (relativePath.StartsWith("/"))
                {
                    relativePath = relativePath.Substring(1);
                }
                var targetPath = Path.Combine(copyPath, relativePath);
                CreateDir(targetPath);
            }
            var files = Directory.GetFiles(originPath, "*.*", SearchOption.AllDirectories);
            foreach (var file in files)
            {
                var relativePath = ConvertToRelativePath(originPath, file);
                if (relativePath.StartsWith("/"))
                {
                    relativePath = relativePath.Substring(1);
                }
                var targetPath = Path.Combine(copyPath, relativePath);
                CopyFile(file, targetPath, force);
            }
        }

        /// <summary>
        /// 删除指定文件夹
        /// </summary>
        public static void DeleteDir(string path, bool keepDir = false)
        {
            if (Directory.Exists(path))
            {
                Directory.Delete(path, true);
                if (keepDir)
                {
                    Directory.CreateDirectory(path);
                }
            }
        }

        /// <summary>
        /// 删除所有空文件夹
        /// </summary>
        public static void DeleteEmptyDir(string path)
        {
            if (!Directory.Exists(path))
            {
                return;
            }
            var dirs = Directory.GetDirectories(path, "*", SearchOption.AllDirectories);
            foreach (var dir in dirs)
            {
                if (Directory.Exists(dir))
                {
                    if (Directory.GetFiles(dir, "*.*", SearchOption.AllDirectories).Length == 0)
                    {
                        Directory.Delete(dir, true);
                    }
                }
            }
        }
        
        public static void SaveFile(string path, byte[] bytes)
        {
            CreateDirByFilePath(path);
            File.WriteAllBytes(path, bytes);
        }
        
        public static void SaveTxtFile(string path, string content)
        {
            CreateDirByFilePath(path);
            File.WriteAllBytes(path, UTF8Encoding.UTF8.GetBytes(content));
        }
        
        /// <summary>
        /// 文件拷贝
        /// </summary>
        public static void CopyFile(string originPath, string copyPath, bool force = true)
        {
            if (File.Exists(copyPath))
            {
                if (!force)
                {
                    return;
                }
                File.Delete(copyPath);
            }
            CreateDirByFilePath(copyPath);
            File.Copy(originPath, copyPath);
        }
        
        /// <summary>
        /// 文件重命名
        /// </summary>
        public static void RenameFile(string originPath, string newPath, bool force = false)
        {
            if (File.Exists(newPath))
            {
                if (!force)
                {
                    return;
                }
                File.Delete(newPath);
            }
            CreateDirByFilePath(newPath);
            File.Move(originPath, newPath);
        }

        public static void DeleteFile(string path)
        {
            if (!File.Exists(path))
            {
                return;
            }
            File.Delete(path);
        }
        
        /// <summary>
        /// 将项目内的绝对路径转相对路径
        /// </summary>
        public static string ConvertToRelativePath(string path)
        {
            return ConvertToRelativePath(ProjectPath, path);
        }
        
        /// <summary>
        /// 将项目内的绝对路径转相对路径
        /// </summary>
        public static string ConvertToRelativePath(string basePath, string path)
        {
            path = FormatPath(path);
            if (!path.StartsWith(basePath))
            {
                return path;
            }
            return path.Replace(basePath, "");
        }
        
        /// <summary>
        /// 加载窗口的 UXML 文件
        /// </summary>
        public static VisualTreeAsset LoadWindowUXML<T>() where T : class
        {
            var windowType = typeof(T);
            if (_uxmlDic.TryGetValue(windowType, out string uxmlGUID))
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(uxmlGUID);
                if (string.IsNullOrEmpty(assetPath))
                {
                    _uxmlDic.Clear();
                    throw new System.Exception($"Invalid UXML GUID : {uxmlGUID} ! Please close the window and open it again !");
                }
                var treeAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(assetPath);
                return treeAsset;
            }
            string[] guids = AssetDatabase.FindAssets(windowType.Name);
            if (guids.Length == 0)
            {
                throw new System.Exception($"Not found any assets : {windowType.Name}");
            }
            foreach (string assetGUID in guids)
            {
                string assetPath = AssetDatabase.GUIDToAssetPath(assetGUID);
                var assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
                if (assetType == typeof(VisualTreeAsset))
                {
                    _uxmlDic.Add(windowType, assetGUID);
                    var treeAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(assetPath);
                    return treeAsset;
                }
            }
            throw new System.Exception($"Not found UXML file : {windowType.Name}");
        }

        public static bool IsMeta(string file)
        {
            var ext = Path.GetExtension(file).ToLower();
            return ext == ".meta";
        }

        public static string GetMetaFilePath(string filePath)
        {
            return filePath + ".meta";
        }
        
        public static string FormatPath(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return path;
            }
            return path.Replace('\\', '/');
        }

        /// <summary>
        /// 将字符串转码为 MD5 码
        /// </summary>
        public static string GetTxtMD5Code(string text)
        {
            var bytes = new UTF8Encoding().GetBytes(text);
            return GetMD5Code(bytes);
        }

        /// <summary>
        /// 将文件转码为 MD5 码
        /// </summary>
        public static string GetFileMD5Code(string path)
        {
            var bytes = File.ReadAllBytes(path);
            return GetMD5Code(bytes);
        }

        /// <summary>
        /// 将字节转换为 MD5 码
        /// </summary>
        public static string GetMD5Code(byte[] bytes)
        {
            var md5 = new MD5CryptoServiceProvider();
            byte[] hashBytes = md5.ComputeHash(bytes);
            
            var hashString = String.Empty;
            for (int i = 0; i < hashBytes.Length; i++)
            {
                hashString += Convert.ToString(hashBytes[i], 16).PadLeft(2, '0');
            }
            return hashString.PadLeft(32, '0');
        }
        
        /// <summary>
        /// 拷贝资源
        /// 使用 AssetDatabase.CopyAsset 拷贝时如果目标文件已存在，会导致目标文件的 guid 变更，使用该方法不会出现 guid 变更
        /// </summary>
        public static void CopyAsset(string path, string newPath)
        {
            CreateDirByFilePath(newPath);
            var meta_newPath = GetMetaFilePath(newPath);
            if (File.Exists(newPath) && File.Exists(meta_newPath))
            {
                File.Copy(path, newPath, true);
                var guid = AssetDatabase.AssetPathToGUID(newPath);
                var oldGuid = AssetDatabase.AssetPathToGUID(path);
                var artMetaContent = File.ReadAllText(GetMetaFilePath(path));
                artMetaContent = artMetaContent.Replace("guid: " + oldGuid, "guid: " + guid);
                File.WriteAllBytes(meta_newPath, UTF8Encoding.UTF8.GetBytes(artMetaContent));
                AssetDatabase.ImportAsset(newPath);
            }
            else
            {
                AssetDatabase.CopyAsset(path, newPath);
            }
        }

        /// <summary>
        /// 删除指定目录下的所有空文件夹
        /// </summary>
        public static void RemoveEmptyFolder(string dir)
        {
            var dirs = Directory.GetDirectories(dir);
            foreach (string d in dirs)
            {
                RemoveEmptyFolder(d);
            }
            if (Directory.GetFiles(dir).Length == 0 && Directory.GetDirectories(dir).Length == 0)
            {
                Directory.Delete(dir, false);
            }
        }

        /// <summary>
        /// 删除 Unity Assets 目录中指定路径下的所有空文件夹
        /// </summary>
        public static void RemoveEmptyUnityFolder(string dir)
        {
            var di = new DirectoryInfo(dir);
            var dis = new List<DirectoryInfo>();
            DoRemoveEmptyUnityFolder(di, dis);
            if (dis.Count > 0)
            {
                foreach (var target in dis)
                {
                    if (File.Exists(target.FullName + ".meta"))
                    {
                        File.Delete(target.FullName + ".meta");
                    }
                    target.Delete(true);
                }
                AssetDatabase.Refresh();
            }
        }
        
        private static bool DoRemoveEmptyUnityFolder(DirectoryInfo target, List<DirectoryInfo> dis)
        {
            var hasDirOrFile = false;
            foreach (var di in target.GetDirectories())
            {
                var result = DoRemoveEmptyUnityFolder(di, dis);
                if (result)
                {
                    hasDirOrFile = true;
                }
            }
            foreach(var fi in target.GetFiles())
            {
                if (!fi.Name.StartsWith(".") && !fi.FullName.EndsWith(".meta"))
                {
                    hasDirOrFile = true;
                }
            }
            if (hasDirOrFile == false)
            {
                if (dis.Contains(target) == false)
                {
                    dis.Add(target);
                }
            }
            return hasDirOrFile;
        }

        /// <summary>
        /// 获取包围盒
        /// </summary>
        public static Bounds GetBounds(GameObject gameObject)
        {
            var renderers = gameObject.GetComponentsInChildren<MeshRenderer>();
            var isFirst = true;
            var bounds = new Bounds();
            foreach (var renderer in renderers) {
                if (isFirst) {
                    isFirst = false;
                    bounds = renderer.bounds;
                    continue;
                }
                bounds.Encapsulate(renderer.bounds);
            }
            return bounds;
        }

        /// <summary>
        /// 设置存储为内置 Mesh 资源类型的文件的 isReadable 属性
        /// </summary>
        public static bool SetMeshAssetReadable(string assetPath, bool isReadable)
        {
            var meshText = File.ReadAllText(assetPath);
            var matchStr = "";
            var replaceStr = "";
            if (isReadable)
            {
                matchStr = "m_IsReadable: 0";
                replaceStr = "m_IsReadable: 1";
            }
            else
            {
                matchStr = "m_IsReadable: 1";
                replaceStr = "m_IsReadable: 0";
            }
            if (!meshText.Contains(matchStr))
            {
                return false;
            }
            meshText = meshText.Replace(matchStr, replaceStr);
            File.WriteAllText(assetPath,meshText);
            return true;
        }

        /// <summary>
        /// 查找当前类型及父类型的字段信息，支持私有化字段
        /// </summary>
        public static FieldInfo GetFieldInfo(Type type, string name, BindingFlags bindingAttr)
        {
            var curType = type;
            while (true)
            {
                var field = curType.GetField(name, bindingAttr);
                if (field != null)
                {
                    return field;
                }
                curType = curType.BaseType;
                if (curType == null)
                {
                    break;
                }
            }
            return null;
        }
        
        /// <summary>
        /// 查找当前类型及父类型的属性信息，支持私有化属性
        /// </summary>
        public static PropertyInfo GetPropertyInfo(Type type, string name, BindingFlags bindingAttr)
        {
            var curType = type;
            while (true)
            {
                var field = curType.GetProperty(name, bindingAttr);
                if (field != null)
                {
                    return field;
                }
                curType = curType.BaseType;
                if (curType == null)
                {
                    break;
                }
            }
            return null;
        }
        
        /// <summary>
        /// GUI 绘制非公用字段属性
        /// </summary>
        public static bool DrawNonPublicField(Object target, string fieldName, bool editEnable = true)
        {
            var type = target.GetType();
            var bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            var field = GetFieldInfo(type, fieldName, bindingAttr);
            var property = GetPropertyInfo(type, fieldName, bindingAttr);
            
            var targetType = field == null ? property.PropertyType : field.FieldType;
            
            var flag = field != null || property.CanWrite;
            var obj1 = field == null ? property.GetValue(target) : field.GetValue(target);
            var obj2 = obj1;
            var str = ObjectNames.NicifyVariableName(fieldName);
            
            EditorGUI.BeginChangeCheck();
            
            EditorGUI.BeginDisabledGroup(!editEnable);
            if (targetType == typeof(bool))
            {
                obj2 = EditorGUILayout.Toggle(str, (bool)obj1);
            }
            else if (targetType == typeof(int))
            {
                obj2 = EditorGUILayout.IntField(str, (int)obj1);
            }
            else if (targetType == typeof(float))
            {
                obj2 = EditorGUILayout.FloatField(str, (float)obj1);
            }
            else if (targetType == typeof(string))
            {
                obj2 = EditorGUILayout.TextField(str, (string)obj1);
            }
            else if (typeof(Object).IsAssignableFrom(targetType))
            {
                obj2 = EditorGUILayout.ObjectField(str, (Object)obj1, targetType, false);
            }
            else if (targetType.IsEnum)
            {
                obj2 = EditorGUILayout.EnumFlagsField(str, (Enum)obj1);
            }
            EditorGUI.EndDisabledGroup();
            
            if (((!EditorGUI.EndChangeCheck() ? 0 : (obj1 != obj2 ? 1 : 0)) & (flag ? 1 : 0)) == 0)
            {
                return false;
            }
            
            Undo.RegisterCompleteObjectUndo(target, fieldName + " Value Changed");
            
            if (property != null)
            {
                property.SetValue(target, obj2);
            }
            else if (field != null)
            {
                field.SetValue(target, obj2);
            }
            
            EditorUtility.SetDirty(target);
            
            return true;
        }
    }
}
